// Electron 主进程入口文件
const { app, BrowserWindow, ipcMain, session, dialog } = require('electron')
const { join } = require('node:path')
const path = require('node:path')
const logger = require('electron-log')
const http = require('node:http')
const { autoUpdater } = require('electron-updater')

// Web 端通信端口（可在需要时修改）
const webCommPort = 8888

function sleep(ms) {
  return new Promise((resolve) => setTimeout(resolve, ms))
}

async function closeWindowAndWait(win, timeout = 3000) {
  if (!win || win.isDestroyed()) {
    return true
  }

  return new Promise((resolve) => {
    let finished = false

    const timer = setTimeout(() => {
      if (!finished) {
        finished = true
        resolve(false)
      }
    }, timeout)

    const cleanup = () => {
      if (!finished) {
        finished = true
        clearTimeout(timer)
        resolve(true)
      }
    }

    win.once('closed', cleanup)

    try {
      win.close()
    } catch (error) {
      logger.error('关闭窗口时出错:', error)
      cleanup()
    }
  })
}

async function waitForTabSystemReady(window, timeout = 2000) {
  if (!window || window.isDestroyed()) {
    return false
  }

  try {
    return await window.webContents.executeJavaScript(`
      (() => {
        if (window.__tabSystemReady) {
          return true
        }

        return new Promise((resolve) => {
          const cleanup = () => {
            window.removeEventListener('tab-system-ready', handler)
            clearTimeout(timer)
          }

          const handler = () => {
            cleanup()
            resolve(true)
          }

          const timer = setTimeout(() => {
            cleanup()
            resolve(false)
          }, ${timeout})

          window.addEventListener('tab-system-ready', handler, { once: true })
        })
      })()
    `)
  } catch (error) {
    logger.error('等待标签系统就绪时出错:', error)
    return false
  }
}

async function waitForWebviewReady(window, shopId, timeout = 3000) {
  if (!window || window.isDestroyed()) {
    return false
  }

  const safeShopId = JSON.stringify(shopId)

  try {
    return await window.webContents.executeJavaScript(`
      (() => {
        const targetShopId = ${safeShopId}
        if (window.__webviewReadySet && window.__webviewReadySet.has(targetShopId)) {
          return true
        }

        return new Promise((resolve) => {
          const cleanup = () => {
            window.removeEventListener('webview-ready', handler)
            clearTimeout(timer)
          }

          const handler = (event) => {
            const detail = event && event.detail
            if (!detail || detail.shopId !== targetShopId) {
              return
            }
            cleanup()
            resolve(true)
          }

          const timer = setTimeout(() => {
            cleanup()
            resolve(false)
          }, ${timeout})

          window.addEventListener('webview-ready', handler)
        })
      })()
    `)
  } catch (error) {
    logger.error('等待 webview 就绪时出错:', error)
    return false
  }
}

// ==================== 自动更新配置 ====================

// 配置自动更新日志
autoUpdater.logger = logger
autoUpdater.logger.transports.file.level = 'info'

// 自动更新设置
autoUpdater.autoDownload = false // 不自动下载，让用户选择
autoUpdater.autoInstallOnAppQuit = true // 退出时自动安装更新

// 格式化字节数辅助函数
function formatBytes(bytes) {
  if (!bytes || bytes === 0) return '0 B'
  const k = 1024
  const sizes = ['B', 'KB', 'MB', 'GB']
  const i = Math.floor(Math.log(bytes) / Math.log(k))
  return Math.round(bytes / Math.pow(k, i) * 100) / 100 + ' ' + sizes[i]
}

// 自动更新事件处理
autoUpdater.on('checking-for-update', () => {
  logger.info('========== 开始检查更新 ==========')
  logger.info('当前版本:', app.getVersion())
  logger.info('更新服务器 URL:', autoUpdater.getFeedURL())
  logger.info('正在检查更新...')
  // 通知渲染进程
  const mainWindow = getMainWindow()
  if (mainWindow) {
    mainWindow.webContents.send('checking-for-update')
  }
})

autoUpdater.on('update-available', (info) => {
  logger.info('========== 发现新版本 ==========')
  logger.info('当前版本:', app.getVersion())
  logger.info('新版本:', info.version)
  logger.info('发布说明:', info.releaseNotes || '无')
  logger.info('发布时间:', info.releaseDate || '无')
  logger.info('更新详情:', JSON.stringify(info, null, 2))
  logger.info('================================')
  // 通知渲染进程显示更新提示
  const mainWindow = getMainWindow()
  if (mainWindow) {
    mainWindow.webContents.send('update-available', {
      version: info.version,
      releaseNotes: info.releaseNotes
    })
  }
})

autoUpdater.on('update-not-available', (info) => {
  logger.info('========== 检查更新结果 ==========')
  logger.info('当前版本:', app.getVersion())
  logger.info('服务器最新版本:', info.version || '未知')
  logger.info('当前已是最新版本，无需更新')
  logger.info('==================================')
  // 通知渲染进程
  const mainWindow = getMainWindow()
  if (mainWindow) {
    mainWindow.webContents.send('update-not-available', info)
  }
})

autoUpdater.on('error', (err) => {
  logger.error('========== 自动更新错误 ==========')
  logger.error('错误信息:', err.message)
  logger.error('错误代码:', err.code || '无')
  logger.error('错误堆栈:', err.stack)
  logger.error('完整错误对象:', JSON.stringify(err, null, 2))
  logger.error('==================================')
})

autoUpdater.on('download-progress', (progressObj) => {
  const percent = Math.round(progressObj.percent || 0)
  const transferred = formatBytes(progressObj.transferred || 0)
  const total = formatBytes(progressObj.total || 0)
  const speed = progressObj.bytesPerSecond ? formatBytes(progressObj.bytesPerSecond) + '/s' : '未知'
  
  logger.info(`[下载进度] ${percent}% - ${transferred} / ${total} - 速度: ${speed}`)
  
  // 通知渲染进程更新下载进度
  const mainWindow = getMainWindow()
  if (mainWindow) {
    mainWindow.webContents.send('download-progress', progressObj)
  }
})

autoUpdater.on('update-downloaded', (info) => {
  logger.info('========== 更新下载完成 ==========')
  logger.info('版本:', info.version || '未知')
  logger.info('发布时间:', info.releaseDate || '未知')
  logger.info('文件路径:', info.path || '未知')
  logger.info('更新信息:', JSON.stringify(info, null, 2))
  logger.info('准备安装更新')
  logger.info('================================')
  // 通知渲染进程显示安装提示
  const mainWindow = getMainWindow()
  if (mainWindow && !mainWindow.isDestroyed()) {
    try {
      mainWindow.webContents.send('update-downloaded', {
        version: info.version || '新版本'
      })
      logger.info('已发送 update-downloaded 事件到渲染进程')
    } catch (error) {
      logger.error('发送 update-downloaded 事件失败:', error)
    }
  } else {
    logger.warn('主窗口不存在或已销毁，无法发送 update-downloaded 事件')
  }
})

// IPC 处理：检查更新
ipcMain.handle('check-for-update', async () => {
  try {
    logger.info('[手动检查更新] 开始检查更新...')
    logger.info('[手动检查更新] 当前版本:', app.getVersion())
    logger.info('[手动检查更新] 更新服务器 URL:', autoUpdater.getFeedURL())
    const result = await autoUpdater.checkForUpdates()
    logger.info('[手动检查更新] 检查结果:', JSON.stringify(result, null, 2))
    return { success: true, message: '正在检查更新' }
  } catch (error) {
    logger.error('[手动检查更新] 检查更新失败:', error)
    logger.error('[手动检查更新] 错误信息:', error.message)
    logger.error('[手动检查更新] 错误堆栈:', error.stack)
    return { success: false, message: error.message }
  }
})

// IPC 处理：下载更新
ipcMain.handle('download-update', async () => {
  try {
    logger.info('[下载更新] 开始下载更新...')
    await autoUpdater.downloadUpdate()
    logger.info('[下载更新] 下载任务已启动')
    return { success: true, message: '开始下载更新' }
  } catch (error) {
    logger.error('[下载更新] 下载更新失败:', error)
    logger.error('[下载更新] 错误信息:', error.message)
    logger.error('[下载更新] 错误堆栈:', error.stack)
    return { success: false, message: error.message }
  }
})

// IPC 处理：安装更新
ipcMain.handle('install-update', () => {
  logger.info('[安装更新] 准备安装更新，应用即将退出...')
  autoUpdater.quitAndInstall(false, true)
  return { success: true, message: '准备安装更新' }
})

// 用于管理多个打开的店铺标签页
// key: shopId, value: { partition, shopName, url, windowId, cookies, cookieDomains }
const shopTabs = new Map()
let webCommServer = null // Web 端通信服务器
const AUTHORIZATION_SHOP_ID = '__authorization__'
let currentAuthorizationTask = null
// 多窗口管理：key: windowId, value: BrowserWindow
const windows = new Map()
let nextWindowId = 1 // 窗口ID自增计数器

/**
 * 解析 Cookie 字符串为 Map
 */
function parseCookie(cookie) {
  const map = new Map()
  for (const item of cookie.split(/\s*;\s*/)) {
    if (item.length === 0) {
      continue
    }
    const [key, value] = item.split(/\s*=\s*/)
    if (key === undefined || value === undefined || key.length === 0) {
      continue
    }
    map.set(key.trim(), value.trim())
  }
  return map
}

/**
 * 获取图标路径（根据平台返回合适的图标文件）
 * @returns {string} 图标文件路径
 */
function getIconPath() {
  const platform = process.platform
  const iconsDir = join(__dirname, '../icons')
  
  if (platform === 'win32') {
    // Windows: 使用 ico256x256 或最大的可用图标
    return join(iconsDir, 'ico256x256.png')
  } else if (platform === 'darwin') {
    // macOS: 使用 ico256x256 或最大的可用图标
    return join(iconsDir, 'ico256x256.png')
  } else {
    // Linux: 使用 ico256x256 或最大的可用图标
    return join(iconsDir, 'ico256x256.png')
  }
}

/**
 * 获取主窗口（第一个窗口，windowId = 1）
 * @returns {BrowserWindow|null}
 */
function getMainWindow() {
  return windows.get(1) || null
}

/**
 * 根据 windowId 获取窗口
 * @param {number} windowId
 * @returns {BrowserWindow|null}
 */
function getWindowById(windowId) {
  return windows.get(windowId) || null
}

/**
 * 创建窗口（包含标签页界面）
 * @param {number} windowId - 窗口ID，如果为 null 则自动分配
 * @param {string} title - 窗口标题，默认为"闪零门店助手"
 * @param {boolean} isDetached - 是否是分离窗口（单标签页窗口），默认 false
 * @returns {BrowserWindow} 创建的窗口
 */
function createWindow(windowId = null, title = '闪零门店助手', isDetached = false) {
  // 如果没有指定 windowId，自动分配
  if (windowId === null) {
    // 重要：分离窗口不能使用 windowId=1（主窗口专用）
    // 如果 nextWindowId 是 1，且要创建分离窗口，则跳过 1
    if (isDetached && nextWindowId === 1) {
      nextWindowId = 2 // 从 2 开始分配
    }
    windowId = nextWindowId++
    
    // 确保分离窗口不会使用 windowId=1
    if (isDetached && windowId === 1) {
      logger.error(`错误：分离窗口被分配了 windowId=1，这是不允许的，重新分配`)
      windowId = nextWindowId++
    }
  } else {
    // 如果指定了 windowId，验证分离窗口不能使用 windowId=1
    if (isDetached && windowId === 1) {
      logger.error(`错误：尝试创建 windowId=1 的分离窗口，这是不允许的`)
      throw new Error('分离窗口不能使用 windowId=1，这是主窗口专用')
    }
  }
  
  // 如果 windowId=1 的位置已经被占用，且要创建的是非分离窗口（主窗口），先清理
  if (windowId === 1 && !isDetached) {
    const existingWindow = windows.get(1)
    if (existingWindow && existingWindow.isDetached === true) {
      existingWindow.close()
      // 立即从 Map 中移除，窗口关闭事件会再次清理
      windows.delete(1)
    }
  }

  // 根据平台设置图标路径
  const iconPath = getIconPath()
  
  const window = new BrowserWindow({
    width: 1920,
    height: 1080,
    title: title,
    icon: iconPath, // 设置窗口图标（左上角图标）
    autoHideMenuBar: true,
    webPreferences: {
      nodeIntegration: true, // 暂时设为 true，以便在 HTML 中使用 require
      contextIsolation: false, // 暂时设为 false，以便直接访问
      webviewTag: true // 启用 webview 标签
    }
  })

  // 将 windowId 存储到窗口对象中，方便后续使用
  window.windowId = windowId
  // 标记是否是分离窗口
  window.isDetached = isDetached

  // 根据窗口类型加载不同的 HTML
  if (isDetached) {
    // 分离窗口：加载简化的 HTML（只显示单个 webview）
    window.loadFile(join(__dirname, '../public/detached.html'))
  } else {
    // 主窗口：加载完整的标签页管理界面
    window.loadFile(join(__dirname, '../public/index.html'))
  }

  // 开发时可以打开调试工具（调试时取消注释）
  if (process.env.NODE_ENV === 'development') {
    window.webContents.openDevTools()
  }

  // 窗口关闭时清理
  window.on('closed', () => {
    // 清理该窗口的所有标签页记录
    for (const [shopId, tabInfo] of shopTabs.entries()) {
      if (tabInfo.windowId === windowId) {
        const isAuthorizationTab = Boolean(tabInfo.isAuthorization)
        const authorizationSystemClosing = Boolean(tabInfo.authorizationSystemClosing)
        shopTabs.delete(shopId)
        if (isAuthorizationTab && !authorizationSystemClosing) {
          handleAuthorizationTabClosed('MAIN_WINDOW_CLOSED')
        }
      }
    }
    // 从 windows Map 中移除
    windows.delete(windowId)
  })

  // 存储到 windows Map
  windows.set(windowId, window)

  return window
}

/**
 * 创建主窗口（包含标签页界面）
 * 这是应用启动时创建的第一个窗口，windowId = 1
 */
function createMainWindow() {
  return createWindow(1, '闪零门店助手')
}

/**
 * 创建用于接收 Web 端登录指令的本地 HTTP 服务器
 */
function createWebCommServer() {
  const server = http.createServer(async (req, res) => {
    // 设置 CORS 头，允许跨域请求
    res.setHeader('Access-Control-Allow-Origin', '*')
    res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS')
    res.setHeader('Access-Control-Allow-Headers', 'Content-Type')
    res.setHeader('Content-Type', 'application/json; charset=utf-8')

    // 处理 OPTIONS 预检请求
    if (req.method === 'OPTIONS') {
      res.writeHead(200)
      res.end()
      return
    }

    // 只处理 POST 请求
    if (req.method !== 'POST') {
      res.writeHead(405)
      res.end(JSON.stringify({ success: false, message: '只支持 POST 请求' }))
      return
    }

    let body = ''
    req.on('data', chunk => {
      body += chunk.toString()
    })

    req.on('end', async () => {
      try {
        const data = JSON.parse(body)

        let result

        try {
          const action = (data && typeof data.action === 'string') ? data.action.toLowerCase() : 'login'
          if (action === 'authorize') {
            result = await handleAuthorizationCommand(data)
          } else {
            result = await handleWebLoginCommand(data)
          }
        } catch (commandError) {
          logger.error('处理指令时发生异常:', commandError)
          result = {
            success: false,
            message: commandError.message || '处理指令时出现异常'
          }
        }

        res.writeHead(200)
        res.end(JSON.stringify(result))
      } catch (error) {
        logger.error('处理 Web 端指令失败:', error)
        res.writeHead(500)
        res.end(JSON.stringify({ success: false, message: error.message }))
      }
    })
  })

  server.listen(webCommPort, '127.0.0.1', () => {
    // Web 端通信服务器已启动
  })

  server.on('error', (error) => {
    if (error.code === 'EADDRINUSE') {
      logger.error(`端口 ${webCommPort} 已被占用，请修改端口号或关闭占用该端口的程序`)
    }
  })

  return server
}

/**
 * 处理 Web 端发送的登录指令
 * @param {Object} data - 登录数据
 * @param {string} data.url - 要打开的网页地址
 * @param {string} data.cookies - Cookie 字符串（格式: key1=value1; key2=value2）
 * @param {string} data.shopId - 店铺唯一标识（用于多开隔离）
 * @param {string} data.shopName - 店铺名称（用于窗口标题）
 * @param {Object} data.cookieDomains - Cookie 域名配置（可选）
 */
async function handleWebLoginCommand(data) {
  let {
    url,
    cookies,
    shopId,
    shopName,
    shopIcon,
    cookieDomains,
    isAuthorization = false
  } = data

  if (!url) {
    const error = new Error('缺少必填参数: url')
    throw error
  }

  if (!shopId) {
    const error = new Error('缺少必填参数: shopId')
    throw error
  }

  // ==================== 饿了么 URL 特殊处理 ====================
  // 如果传入的 URL 是饿了么的基础 URL（melody.shop.ele.me），且 cookie 中有 shopId
  // 自动转换为完整的 dashboard URL：https://melody.shop.ele.me/app/chain/{shopId}/dashboard#app.chainshop.dashboard
  if (url && cookies) {
    try {
      const urlObj = new URL(url)
      const isElemeUrl = urlObj.hostname === 'melody.shop.ele.me'
      
      if (isElemeUrl) {
        // 解析 cookie 字符串，查找 shopId
        const cookieMap = parseCookie(cookies)
        const elemeShopId = cookieMap.get('shopId')
        
        if (elemeShopId) {
          url = `https://melody.shop.ele.me/app/shop/${elemeShopId}/dashboard#app.shop.dashboard`
        }
      }
    } catch (error) {
      // 出错时继续使用原始 URL
    }
  }

  // 激活主窗口：显示并前置窗口，使其获取焦点
  // 方案A：总是创建在主窗口（第一个窗口，windowId = 1）
  let mainWindow = getMainWindow()
  
  // 如果主窗口不存在，或者是分离窗口，需要创建或重新创建主窗口
  if (!mainWindow || (mainWindow.isDetached === true)) {
    // 如果主窗口存在但是分离窗口，需要关闭它（因为主窗口应该是标签页窗口）
    if (mainWindow && mainWindow.isDetached === true) {
      await closeWindowAndWait(mainWindow)
      // 确保从 Map 中移除
      windows.delete(1)
    }

    // 再次检查：如果 windowId=1 的位置仍然被占用（可能是其他窗口），强制清理
    const existingWindowAt1 = windows.get(1)
    if (existingWindowAt1) {
      await closeWindowAndWait(existingWindowAt1)
      // 确保从 Map 中移除
      windows.delete(1)
    }

    // 创建主窗口（确保 windowId = 1，isDetached = false）
    // createMainWindow() 会调用 createWindow(1, ...)，其中已经包含了清理逻辑
    mainWindow = createMainWindow()
    
    // 验证主窗口确实被正确存储
    const verifyMainWindow = windows.get(1)
    if (!verifyMainWindow || verifyMainWindow !== mainWindow) {
      logger.error(`主窗口存储验证失败: verifyMainWindow=${!!verifyMainWindow}, verifyMainWindow===mainWindow=${verifyMainWindow === mainWindow}`)
      return {
        success: false,
        message: '主窗口存储失败',
        shopId,
        error: '主窗口创建后无法正确存储到 windows Map'
      }
    }
    
    // 等待主窗口加载完成
    await new Promise(resolve => {
      if (mainWindow.webContents.isLoading()) {
        mainWindow.webContents.once('did-finish-load', resolve)
      } else {
        resolve()
      }
    })

    const tabSystemReady = await waitForTabSystemReady(mainWindow)
    if (!tabSystemReady) {
      logger.error('主窗口标签系统初始化超时')
      return {
        success: false,
        message: '主窗口标签系统初始化超时',
        shopId,
        error: 'tab-system-ready timeout'
      }
    }
    
    // 验证主窗口加载的是正确的 HTML
    const currentUrl = mainWindow.webContents.getURL()
    if (currentUrl.includes('detached.html')) {
      logger.error('错误：主窗口加载了 detached.html，应该是 index.html')
      return {
        success: false,
        message: '主窗口加载了错误的页面',
        shopId,
        error: '主窗口应该是标签页窗口，但加载了分离窗口页面'
      }
    }
  }

  const ensuredTabSystemReady = await waitForTabSystemReady(mainWindow)
  if (!ensuredTabSystemReady) {
    logger.error('主窗口标签系统未就绪')
    return {
      success: false,
      message: '主窗口标签系统未就绪',
      shopId,
      error: 'tab-system-ready timeout'
    }
  }
  
  // 再次验证主窗口是否正确（不是分离窗口）
  if (!mainWindow || mainWindow.windowId !== 1 || mainWindow.isDetached === true) {
    logger.error(`主窗口验证失败: windowId=${mainWindow?.windowId}, isDetached=${mainWindow?.isDetached}`)
    return {
      success: false,
      message: '主窗口验证失败',
      shopId,
      error: '主窗口不是有效的标签页窗口'
    }
  }
  
  // 确保主窗口激活并前置
  if (mainWindow) {
    // 如果窗口最小化，先恢复
    if (mainWindow.isMinimized()) {
      mainWindow.restore()
    }

    // 显示窗口（如果隐藏则显示）
    if (!mainWindow.isVisible()) {
      mainWindow.show()
    }

    // 将窗口移到最前面并获取焦点
    mainWindow.focus()

    // macOS 特殊处理：确保应用在前台
    if (process.platform === 'darwin') {
      app.dock.show()
    }
  } else {
    // 如果主窗口仍然不存在，返回错误
    return {
      success: false,
      message: '无法创建或获取主窗口',
      shopId,
      error: '主窗口不存在'
    }
  }

  // 使用 shopId 创建唯一的 partition，实现 Cookie 隔离
  const partition = `persist:web_shop_${shopId}`
  const windowTitle = shopName || `店铺-${shopId}`

  // 检查是否已经存在该店铺标签页
  if (shopTabs.has(shopId)) {
    // 标签页已存在，只切换标签页，不重新加载
    switchToTab(shopId)
    return {
      success: true,
      message: '标签页已存在，已切换',
      shopId,
      action: 'switch'
    }
  }

  // 设置 Cookie
  if (cookies) {
    await setCookiesForWindow(partition, url, cookies, cookieDomains)
  }

  // 等待主窗口加载完成后再创建标签页
  if (mainWindow && mainWindow.webContents) {
    // 再次验证：确保主窗口确实是 windowId=1 且不是分离窗口
    const currentMainWindow = getMainWindow()
    if (currentMainWindow !== mainWindow) {
      logger.error(`主窗口对象不一致: currentMainWindow=${!!currentMainWindow}, mainWindow=${!!mainWindow}`)
      if (currentMainWindow) {
        logger.error(`  currentMainWindow: windowId=${currentMainWindow.windowId}, isDetached=${currentMainWindow.isDetached}`)
      }
      if (mainWindow) {
        logger.error(`  mainWindow: windowId=${mainWindow.windowId}, isDetached=${mainWindow.isDetached}`)
      }
      return {
        success: false,
        message: '主窗口对象不一致',
        shopId,
        error: '主窗口对象在验证过程中发生了变化'
      }
    }
    
    // 确保页面已完全加载
    if (mainWindow.webContents.isLoading()) {
      await new Promise(resolve => {
        mainWindow.webContents.once('did-finish-load', resolve)
      })
    }

    // 验证主窗口是否有 createTab 函数
    const hasCreateTab = await mainWindow.webContents.executeJavaScript(`
      typeof window.createTab === 'function'
    `).catch(() => false)
    
    if (!hasCreateTab) {
      logger.error('主窗口没有 createTab 函数，可能加载了错误的页面')
      return {
        success: false,
        message: '主窗口缺少必要的函数',
        shopId,
        error: '主窗口应该加载 index.html，但可能加载了 detached.html'
      }
    }

    // 使用 JSON.stringify 安全传递参数，避免特殊字符问题
    const safeShopId = JSON.stringify(shopId)
    const safeShopName = JSON.stringify(windowTitle)
    const safeShopIcon = JSON.stringify(shopIcon || '')
    const safePartition = JSON.stringify(partition)

    try {
      const result = await mainWindow.webContents.executeJavaScript(`
        (function() {
          return new Promise((resolve) => {
            try {
              if (typeof window.createTab === 'function') {
                const result = window.createTab(${safeShopId}, ${safeShopName}, ${safePartition}, ${safeShopIcon})

                if (result instanceof Promise) {
                  result.then((success) => {
                    resolve({ success: success !== false, created: success })
                  }).catch((e) => {
                    resolve({ success: false, error: e.message, stack: e.stack })
                  })
                } else {
                  resolve({ success: result !== false, created: result })
                }
              } else {
                resolve({ success: false, error: 'window.createTab 函数未定义' })
              }
            } catch (e) {
              resolve({ success: false, error: e.message, stack: e.stack })
            }
          })
        })()
      `)
      
      if (!result) {
        const errorMsg = `标签页 ${shopId} 创建返回 null/undefined`
        logger.error(errorMsg)
        throw new Error(`标签页创建返回无效结果: ${shopId}`)
      }

      if (!result.success) {
        return {
          success: false,
          message: `标签页创建失败: ${shopId}`,
          shopId,
          error: result.error || '创建失败'
        }
      }

      const webviewReady = await waitForWebviewReady(mainWindow, shopId, 5000)
      if (!webviewReady) {
        logger.error(`标签页 ${shopId} 的 webview 就绪超时`)
        return {
          success: false,
          message: `标签页创建失败: ${shopId}`,
          shopId,
          error: 'webview ready timeout'
        }
      }

      const tabExists = await mainWindow.webContents.executeJavaScript(`
        (function() {
          try {
            const tabs = window.tabs || []
            const tab = tabs.find(t => t.shopId === ${safeShopId})
            return tab && tab.webview ? true : false
          } catch (e) {
            return false
          }
        })()
      `).catch(() => false)
      
      if (!tabExists) {
        const errorMsg = `标签页验证失败: ${shopId} - 标签页不存在`
        return {
          success: false,
          message: errorMsg,
          shopId,
          error: '标签页不存在'
        }
      }

      // 方案A：总是创建在主窗口（windowId = 1）
      // 再次验证主窗口的 windowId（确保没有被改变）
      const finalMainWindowId = mainWindow.windowId
      if (finalMainWindowId !== 1) {
        logger.error(`严重错误：主窗口的 windowId 不是 1，而是 ${finalMainWindowId}`)
        return {
          success: false,
          message: `主窗口 windowId 错误: ${finalMainWindowId}，应该是 1`,
          shopId,
          error: '主窗口的 windowId 必须是 1'
        }
      }
      
      // 先设置 shopTabs，这样 loadUrlToWebview 才能找到窗口
      shopTabs.set(shopId, {
        partition,
        shopName: windowTitle,
        url,
        cookies,
        cookieDomains,
        isAuthorization,
        windowId: finalMainWindowId // 主窗口的 windowId 始终为 1
      })

      // 然后加载 URL
      await loadUrlToWebview(shopId, url)

      const successResult = {
        success: true,
        message: '标签页已创建',
        shopId,
        action: 'create',
        partition
      }
      
      return successResult
    } catch (error) {
      return {
        success: false,
        message: `创建标签页失败: ${error.message}`,
        shopId,
        error: error.message
      }
    }
  } else {
    return {
      success: false,
      message: '主窗口不存在',
      shopId,
      error: '主窗口不存在'
    }
  }
}

// ==================== 授权取 Cookie 功能 ====================

function normalizeDomain(domain) {
  if (!domain || typeof domain !== 'string') {
    return ''
  }
  return domain.replace(/^\.+/, '').toLowerCase()
}

function addDomainVariants(targetSet, domain) {
  const normalized = normalizeDomain(domain)
  if (!normalized) {
    return
  }
  targetSet.add(normalized)
  const parts = normalized.split('.')
  if (parts.length >= 2) {
    targetSet.add(parts.slice(-2).join('.'))
  }
}

function collectRelatedDomains(cookieDomain, loginUrl) {
  const related = new Set()

  if (cookieDomain) {
    addDomainVariants(related, cookieDomain)
  }

  if (loginUrl) {
    try {
      const urlObj = new URL(loginUrl)
      addDomainVariants(related, urlObj.hostname)
    } catch (_) {
      // ignore
    }
  }

  const lowerDomain = normalizeDomain(cookieDomain)
  if (lowerDomain.includes('jddj.com')) {
    related.add('jddj.com')
    related.add('jd.com')
  }

  if (lowerDomain.includes('jinritemai.com')) {
    related.add('jinritemai.com')
  }

  if (lowerDomain.includes('meituan.com')) {
    related.add('meituan.com')
  }

  if (lowerDomain.includes('ele.me')) {
    related.add('ele.me')
  }

  return related
}

function shouldIncludeCookie(cookieDomain, relatedDomains) {
  if (!relatedDomains || relatedDomains.size === 0) {
    return true
  }

  const normalized = normalizeDomain(cookieDomain)
  if (!normalized) {
    return false
  }

  for (const domain of relatedDomains) {
    if (normalized === domain) {
      return true
    }
    if (normalized.endsWith(`.${domain}`)) {
      return true
    }
    if (domain.endsWith(`.${normalized}`)) {
      return true
    }
  }

  return false
}

async function collectAuthorizationCookies(partition, cookieDomain, loginUrl) {
  try {
    const ses = session.fromPartition(partition, { cache: false })
    const allCookies = await ses.cookies.get({})
    const relatedDomains = collectRelatedDomains(cookieDomain, loginUrl)

    const dedup = new Map()
    for (const cookie of allCookies) {
      if (!shouldIncludeCookie(cookie.domain, relatedDomains)) {
        continue
      }
      const key = `${cookie.domain || ''}|${cookie.path || ''}|${cookie.name}`
      if (!dedup.has(key)) {
        dedup.set(key, cookie)
      }
    }

    const sortedCookies = Array.from(dedup.values()).sort((a, b) => {
      if (a.name === b.name) {
        return (a.domain || '').localeCompare(b.domain || '')
      }
      return a.name.localeCompare(b.name)
    })

    return sortedCookies.map(c => `${c.name}=${c.value}`).join('; ')
  } catch (error) {
    logger.error('收集授权 Cookie 失败:', error)
    return ''
  }
}

function hasNewAuthCookies(oldCookies, newCookies, cookieDomain, loginKeyword) {
  if (!newCookies || newCookies.length === 0) {
    return false
  }

  const oldHasLoginKeyword = loginKeyword ? (oldCookies || '').includes(loginKeyword) : false
  const newHasLoginKeyword = loginKeyword ? newCookies.includes(loginKeyword) : false
  if (loginKeyword && newHasLoginKeyword && !oldHasLoginKeyword) {
    return true
  }

  if (newCookies.length <= (oldCookies || '').length) {
    return false
  }

  const hasLoginKeyword = loginKeyword && newCookies.includes(loginKeyword)

  if (cookieDomain && cookieDomain.toLowerCase().includes('jddj.com')) {
    if (newCookies.includes('store_session')) {
      return true
    }
  }

  if (cookieDomain && cookieDomain.toLowerCase().includes('jinritemai.com')) {
    if (newCookies.includes('csrf_session_id')) {
      return true
    }
  }

  if (cookieDomain && cookieDomain.toLowerCase().includes('ecom.meituan.com')) {
    const bizTypeMatch = newCookies.match(/bizType\s*=\s*1(?:[;\s]|$)/)
    if (bizTypeMatch !== null) {
      return true
    }
    return false
  }

  if (cookieDomain && cookieDomain.toLowerCase().includes('ele.me')) {
    const lowerCookie = newCookies.toLowerCase()
    if (lowerCookie.includes('ksid')) {
      return true
    }
    if (lowerCookie.includes('wmstoken=')) {
      return true
    }
  }

  return Boolean(hasLoginKeyword)
}

function handleAuthorizationTabClosed(reason) {
  const task = currentAuthorizationTask
  if (!task || task.finished) {
    return
  }

  let message = '授权流程已中断'
  let errorCode = 'UNKNOWN'

  switch (reason) {
    case 'USER_CLOSED':
      message = '登录标签页已关闭，请重新尝试'
      errorCode = 'USER_CLOSED'
      break
    case 'MAIN_WINDOW_CLOSED':
      message = '主窗口已关闭，授权流程被中断'
      errorCode = 'WINDOW_CLOSED'
      break
    case 'TIMEOUT':
      message = '登录超时，请重新尝试'
      errorCode = 'TIMEOUT'
      break
    default:
      errorCode = reason || 'UNKNOWN'
      break
  }

  finalizeAuthorizationTask(task, {
    success: false,
    errorCode,
    message
  })
}

function finalizeAuthorizationTask(task, payload) {
  if (!task || task.finished) {
    return
  }

  task.finished = true

  if (task.interval) {
    clearInterval(task.interval)
  }

  if (task.timeoutTimer) {
    clearTimeout(task.timeoutTimer)
  }

  if (task.partition) {
    try {
      const ses = session.fromPartition(task.partition, { cache: false })
      ses.clearStorageData({ storages: ['cookies'] }).catch(() => { })
    } catch (_) {
      // ignore
    }
  }

  const resolver = task.resolve
  currentAuthorizationTask = null

  if (typeof resolver === 'function') {
    resolver(payload)
  }
}

async function showAuthorizationSuccessDialog(window, shopName) {
  const targetWindow = window || getMainWindow()
  if (!targetWindow) {
    return
  }

  try {
    const title = '授权成功'
    const message = '授权店群成功'
    const detail = '已成功获取登录所需的 Cookie，稍后将自动关闭授权标签页。'

    await dialog.showMessageBox(targetWindow, {
      type: 'info',
      title,
      message,
      detail,
      buttons: ['确定'],
      defaultId: 0,
      noLink: true
    })
  } catch (error) {
    logger.warn('展示授权成功提示框失败:', error)
  }
}

function scheduleAuthorizationTabClose(task) {
  const tabInfo = shopTabs.get(task.shopId)
  if (tabInfo) {
    tabInfo.authorizationSystemClosing = true
    shopTabs.set(task.shopId, tabInfo)
  }

  setTimeout(() => {
    try {
      closeShopTab(task.shopId)
    } catch (error) {
      logger.error('关闭授权标签页失败:', error)
    }
  }, 1500)
}

async function handleAuthorizationCommand(data) {
  if (currentAuthorizationTask && !currentAuthorizationTask.finished) {
    return {
      success: false,
      errorCode: 'BUSY',
      message: '已有授权流程正在进行，请稍后再试'
    }
  }

  const {
    loginUrl,
    cookieDomain,
    loginKeyword,
    timeoutSeconds = 180,
    shopName,
    shopIcon
  } = data || {}

  if (!loginUrl || !cookieDomain || !loginKeyword) {
    return {
      success: false,
      errorCode: 'INVALID_PARAMS',
      message: '缺少必要参数：loginUrl、cookieDomain 或 loginKeyword'
    }
  }

  const sanitizedTimeout = Number.isFinite(timeoutSeconds) ? Math.max(1, Math.floor(timeoutSeconds)) : 180
  const shopId = AUTHORIZATION_SHOP_ID
  const partition = `persist:web_shop_${shopId}`

  const task = {
    shopId,
    partition,
    cookieDomain,
    loginKeyword,
    loginUrl,
    timeoutSeconds: sanitizedTimeout,
    finished: false,
    interval: null,
    timeoutTimer: null,
    resolve: null
  }

  currentAuthorizationTask = task

  try {
    const ses = session.fromPartition(partition, { cache: false })
    try {
      await ses.clearStorageData({ storages: ['cookies', 'localstorage', 'sessionstorage', 'cachestorage'] })
      await ses.clearCache().catch(() => { })
    } catch (clearError) {
      logger.warn('清理授权分区数据失败:', clearError)
    }

    const existingTab = shopTabs.get(shopId)
    if (existingTab) {
      existingTab.authorizationSystemClosing = true
      shopTabs.set(shopId, existingTab)
      closeShopTab(shopId)
    }

    const loginResult = await handleWebLoginCommand({
      url: loginUrl,
      shopId,
      shopName: shopName || '授权登录',
      shopIcon,
      isAuthorization: true
    })

    if (!loginResult || !loginResult.success) {
      currentAuthorizationTask = null
      return loginResult || {
        success: false,
        errorCode: 'OPEN_FAILED',
        message: '创建授权标签页失败'
      }
    }

    const tabInfo = shopTabs.get(shopId)
    if (!tabInfo) {
      currentAuthorizationTask = null
      return {
        success: false,
        errorCode: 'TAB_NOT_FOUND',
        message: '授权标签页未成功创建'
      }
    }

    tabInfo.isAuthorization = true
    tabInfo.authorizationSystemClosing = false
    tabInfo.authorizationMeta = {
      cookieDomain,
      loginKeyword,
      loginUrl
    }
    shopTabs.set(shopId, tabInfo)

    task.partition = tabInfo.partition

    return await new Promise((resolve) => {
      task.resolve = resolve
      let attempts = 0
      let lastCookies = ''
      let checking = false
      const maxAttempts = sanitizedTimeout

      const runCheck = async () => {
        if (task.finished || checking) {
          return
        }
        checking = true
        try {
          const cookiesString = await collectAuthorizationCookies(task.partition, task.cookieDomain, task.loginUrl)
          if (cookiesString && hasNewAuthCookies(lastCookies, cookiesString, task.cookieDomain, task.loginKeyword)) {
            const tabMeta = shopTabs.get(task.shopId)
            if (tabMeta) {
              const win = getWindowById(tabMeta.windowId)
              await showAuthorizationSuccessDialog(win, tabMeta.shopName)
            }
            scheduleAuthorizationTabClose(task)
            finalizeAuthorizationTask(task, {
              success: true,
              message: '授权成功',
              cookies: cookiesString,
              cookieDomain: task.cookieDomain
            })
            return
          }

          lastCookies = cookiesString
          attempts += 1

          if (attempts >= maxAttempts) {
            const tabMeta = shopTabs.get(task.shopId)
            if (tabMeta) {
              tabMeta.authorizationSystemClosing = true
              shopTabs.set(task.shopId, tabMeta)
              closeShopTab(task.shopId)
            }
            finalizeAuthorizationTask(task, {
              success: false,
              errorCode: 'TIMEOUT',
              message: '登录超时，请重新尝试'
            })
          }
        } catch (error) {
          logger.warn('监控授权 Cookie 失败:', error)
          attempts += 1
          if (attempts >= maxAttempts) {
            const tabMeta = shopTabs.get(task.shopId)
            if (tabMeta) {
              tabMeta.authorizationSystemClosing = true
              shopTabs.set(task.shopId, tabMeta)
              closeShopTab(task.shopId)
            }
            finalizeAuthorizationTask(task, {
              success: false,
              errorCode: 'TIMEOUT',
              message: '登录超时，请重新尝试'
            })
          }
        } finally {
          checking = false
        }
      }

      task.interval = setInterval(runCheck, 1000)
      runCheck()
    })
  } catch (error) {
    logger.error('处理授权指令失败:', error)
    handleAuthorizationTabClosed('INTERNAL_ERROR')
    return {
      success: false,
      errorCode: 'INTERNAL_ERROR',
      message: error.message || '授权流程失败'
    }
  }
}

/**
 * 为窗口设置 Cookies
 * @param {string} partition - 会话分区标识
 * @param {string} baseUrl - 基础 URL
 * @param {string} cookieString - Cookie 字符串
 * @param {Object} cookieDomains - 域名配置对象
 */
async function setCookiesForWindow(partition, baseUrl, cookieString, cookieDomains) {
  const ses = session.fromPartition(partition, { cache: false })
  const jar = parseCookie(cookieString)

  // 如果没有指定域名配置，使用默认逻辑
  if (!cookieDomains || Object.keys(cookieDomains).length === 0) {
    // 默认行为：从 baseUrl 提取域名，设置 cookie
    const urlObj = new URL(baseUrl)
    const defaultDomain = urlObj.hostname

    for (const [key, value] of jar) {
      try {
        // 为当前域名设置 cookie
        await ses.cookies.set({
          url: baseUrl,
          name: key,
          value: value
        })

        // 如果有顶级域名，也为顶级域名设置（如 .example.com）
        const topLevelDomain = defaultDomain.startsWith('.')
          ? defaultDomain
          : `.${defaultDomain.split('.').slice(-2).join('.')}`

        if (topLevelDomain !== `.${defaultDomain}`) {
          await ses.cookies.set({
            url: `https://${topLevelDomain.substring(1)}`,
            name: key,
            value: value,
            domain: topLevelDomain
          })
        }
      } catch (error) {
        logger.error(`设置 Cookie 失败 [${key}]:`, error)
      }
    }
  } else {
    // 使用指定的域名配置
    for (const [domain, urls] of Object.entries(cookieDomains)) {
      const domainUrls = Array.isArray(urls) ? urls : [urls]

      for (const targetUrl of domainUrls) {
        for (const [key, value] of jar) {
          try {
            await ses.cookies.set({
              url: targetUrl,
              name: key,
              value: value,
              domain: domain.startsWith('.') ? domain : `.${domain}`
            })
          } catch (error) {
            logger.error(`设置 Cookie 失败 [${domain}/${key}]:`, error)
          }
        }
      }
    }
  }
}

/**
 * 加载 URL 到 webview
 */
async function loadUrlToWebview(shopId, url) {
  const tabInfo = shopTabs.get(shopId)
  if (!tabInfo) {
    logger.error(`[loadUrlToWebview] TabInfo 不存在: shopId=${shopId}`)
    return
  }

  const window = getWindowById(tabInfo.windowId)
  if (!window) {
    logger.error(`[loadUrlToWebview] Window 不存在: shopId=${shopId}, windowId=${tabInfo.windowId}`)
    return
  }

  try {
    // 使用 JSON.stringify 安全传递参数
    const safeShopId = JSON.stringify(shopId)
    const safeUrl = JSON.stringify(url)

    // 等待主窗口完全准备好
    if (window.webContents.isLoading()) {
      await new Promise(resolve => {
        window.webContents.once('did-finish-load', resolve)
      })
    }

    const tabSystemReady = await waitForTabSystemReady(window)
    if (!tabSystemReady) {
      logger.error(`[loadUrlToWebview] 等待标签系统就绪超时: shopId=${shopId}`)
      return
    }

    await window.webContents.executeJavaScript(`
      (function() {
        try {
          if (typeof window.loadShopUrl === 'function') {
            return window.loadShopUrl(${safeShopId}, ${safeUrl})
          } else {
            return false
          }
        } catch (e) {
          throw e
        }
      })()
    `)
  } catch (error) {
    logger.error(`加载 URL 到 webview 失败 [${shopId}]:`, error)
    throw error
  }
}

/**
 * 切换到指定标签页
 */
function switchToTab(shopId) {
  const tabInfo = shopTabs.get(shopId)
  if (!tabInfo) return

  const window = getWindowById(tabInfo.windowId)
  if (!window) return

  const safeShopId = JSON.stringify(shopId)
  window.webContents.executeJavaScript(`
    (function() {
      if (typeof window.switchTab === 'function') {
        window.switchTab(${safeShopId})
      }
    })()
  `).catch(() => { })

  // 激活并聚焦到该窗口
  if (window.isMinimized()) {
    window.restore()
  }
  if (!window.isVisible()) {
    window.show()
  }
  window.focus()
}

/**
 * 重新加载标签页（更新 URL 和 Cookie）
 */
async function reloadTab(shopId, url, cookies, cookieDomains) {
  if (!shopTabs.has(shopId)) return

  const tabInfo = shopTabs.get(shopId)

  // 更新 Cookie
  if (cookies) {
    await setCookiesForWindow(tabInfo.partition, url, cookies, cookieDomains)
    tabInfo.cookies = cookies
    tabInfo.cookieDomains = cookieDomains
  }

  // 更新 URL
  tabInfo.url = url
  await loadUrlToWebview(shopId, url)
}

/**
 * 关闭指定的店铺标签页
 * @param {string} shopId - 店铺 ID
 */
function closeShopTab(shopId) {
  const tabInfo = shopTabs.get(shopId)
  if (!tabInfo) {
    return { success: false, message: '标签页不存在' }
  }

  const isAuthorizationTab = Boolean(tabInfo.isAuthorization)
  const authorizationSystemClosing = Boolean(tabInfo.authorizationSystemClosing)

  const window = getWindowById(tabInfo.windowId)
  if (window) {
    const safeShopId = JSON.stringify(shopId)
    window.webContents.executeJavaScript(`
      (function() {
        if (typeof window.closeTab === 'function') {
          window.closeTab(${safeShopId})
        }
      })()
    `).catch(() => { })
  }

  shopTabs.delete(shopId)

  if (isAuthorizationTab && !authorizationSystemClosing) {
    handleAuthorizationTabClosed('USER_CLOSED')
  }

  return { success: true, message: '标签页已关闭' }
}

// ==================== 自定义协议处理 ====================

// 自定义协议名称（必须与 Web 端保持一致）
const PROTOCOL_NAME = 'linkshop-app'

/**
 * 解析协议 URL 参数
 * @param {string} url - 协议 URL，格式: linkshop-app://login?url=xxx&shopId=xxx&...
 * @returns {Object} 解析后的参数对象
 */
function parseProtocolUrl(url) {
  try {
    // 移除协议前缀 linkshop-app://
    const urlWithoutProtocol = url.replace(`${PROTOCOL_NAME}://`, '')
    
    // 分离路径和查询参数
    const [path, queryString] = urlWithoutProtocol.split('?')
    
    // 清理路径：去除首尾斜杠
    const cleanPath = path ? path.replace(/^\/+|\/+$/g, '') : 'launch'
    
    if (!queryString) {
      // 如果没有查询参数，返回路径作为 action
      return { action: cleanPath || 'launch' }
    }
    
    // 解析查询参数
    const params = new URLSearchParams(queryString)
    const result = {
      action: cleanPath || 'launch'
    }
    
    // 解析各个参数
    if (params.has('url')) {
      result.url = decodeURIComponent(params.get('url'))
    }
    if (params.has('shopId')) {
      result.shopId = params.get('shopId')
    }
    if (params.has('shopName')) {
      result.shopName = decodeURIComponent(params.get('shopName'))
    }
    if (params.has('shopIcon')) {
      result.shopIcon = decodeURIComponent(params.get('shopIcon'))
    }
    if (params.has('cookies')) {
      result.cookies = decodeURIComponent(params.get('cookies'))
    }
    if (params.has('cookieDomains')) {
      try {
        result.cookieDomains = JSON.parse(decodeURIComponent(params.get('cookieDomains')))
      } catch (e) {
        logger.error('解析 cookieDomains 失败:', e)
      }
    }
    
    return result
  } catch (error) {
    logger.error('解析协议 URL 失败:', error)
    return { action: 'launch' }
  }
}

/**
 * 处理协议启动
 * @param {string} url - 协议 URL
 */
async function handleProtocolLaunch(url) {
  const params = parseProtocolUrl(url)
  
  // 如果只是启动应用（ping 或 launch），不执行登录
  // 注意：action 可能带有尾随斜杠，需要同时检查
  const normalizedAction = params.action.replace(/\/+$/, '') // 去除尾随斜杠
  if (normalizedAction === 'ping' || normalizedAction === 'launch') {
    // 确保主窗口存在并激活
    let mainWindow = getMainWindow()
    if (!mainWindow) {
      mainWindow = createMainWindow()
      await new Promise(resolve => {
        if (mainWindow.webContents.isLoading()) {
          mainWindow.webContents.once('did-finish-load', resolve)
        } else {
          resolve()
        }
      })
    }
    
    // 激活窗口
    if (mainWindow.isMinimized()) {
      mainWindow.restore()
    }
    if (!mainWindow.isVisible()) {
      mainWindow.show()
    }
    mainWindow.focus()
    
    // macOS 特殊处理
    if (process.platform === 'darwin') {
      app.dock.show()
    }
    
    // 确保 HTTP 服务器已启动（如果还没启动）
    if (!webCommServer) {
      webCommServer = createWebCommServer()
    }
    
    return
  }
  
  // 如果是登录指令，需要所有必要参数
  // 注意：action 可能带有尾随斜杠，需要同时检查
  if (normalizedAction === 'login') {
    if (!params.url || !params.shopId) {
      logger.error('协议登录缺少必要参数:', params)
      logger.error('参数详情:', JSON.stringify(params, null, 2))
      return
    }
    
    // 等待应用完全启动（如果正在启动）
    await app.whenReady()
    // 确保主窗口存在
    let mainWindow = getMainWindow()
    if (!mainWindow) {
      mainWindow = createMainWindow()
      await new Promise(resolve => {
        if (mainWindow.webContents.isLoading()) {
          mainWindow.webContents.once('did-finish-load', resolve)
        } else {
          resolve()
        }
      })
    } else {
      if (mainWindow.webContents.isLoading()) {
        await new Promise(resolve => {
          mainWindow.webContents.once('did-finish-load', resolve)
        })
      }
    }

    const hasCreateTab = await mainWindow.webContents.executeJavaScript(`
      typeof window.createTab === 'function'
    `).catch(() => false)
    
    if (!hasCreateTab) {
      logger.warn('主窗口缺少 createTab 函数，尝试继续执行登录流程')
    }
    
    // 激活窗口
    if (mainWindow.isMinimized()) {
      mainWindow.restore()
    }
    if (!mainWindow.isVisible()) {
      mainWindow.show()
    }
    mainWindow.focus()
    
    // macOS 特殊处理
    if (process.platform === 'darwin') {
      app.dock.show()
    }
    
    // 使用现有的登录处理函数
    try {
      const result = await handleWebLoginCommand({
        url: params.url,
        cookies: params.cookies,
        shopId: params.shopId,
        shopName: params.shopName,
        shopIcon: params.shopIcon,
        cookieDomains: params.cookieDomains
      })
      
      // 协议登录成功
    } catch (error) {
      logger.error('❌ 协议登录失败:', error)
      // 重新抛出错误，让调用者知道失败
      throw error
    }
  }
}

// ==================== Electron 应用生命周期 ====================

// 注册自定义协议（必须在 app.whenReady() 之前）
if (process.defaultApp) {
  // 开发环境：需要手动注册协议
  if (process.argv.length >= 2) {
    app.setAsDefaultProtocolClient(PROTOCOL_NAME, process.execPath, [path.resolve(process.argv[1])])
  }
} else {
  // 生产环境：自动注册协议
  app.setAsDefaultProtocolClient(PROTOCOL_NAME)
}

// 处理协议启动（macOS 专用）
if (process.platform === 'darwin') {
  app.on('open-url', (event, url) => {
    event.preventDefault()
    handleProtocolLaunch(url)
  })
}

// 单实例锁定（确保只运行一个实例）
const gotTheLock = app.requestSingleInstanceLock()

if (!gotTheLock) {
  app.quit()
} else {
  app.on('second-instance', (event, commandLine, workingDirectory) => {
    // 如果已经运行，聚焦到主窗口
    const mainWindow = getMainWindow()
    if (mainWindow) {
      if (mainWindow.isMinimized()) mainWindow.restore()
      mainWindow.focus()
    }
    
      // Windows/Linux: 检查命令行参数中是否有协议 URL
      if (process.platform === 'win32' || process.platform === 'linux') {
        const protocolUrl = commandLine.find(arg => arg.startsWith(`${PROTOCOL_NAME}://`))
        if (protocolUrl) {
          // 延迟处理，确保主窗口已激活并准备好
          // 增加延迟时间，确保主窗口完全准备好
          setTimeout(() => {
            handleProtocolLaunch(protocolUrl).catch(error => {
              logger.error('处理协议启动失败:', error)
            })
          }, 200)
        }
      }
    
    // macOS: 协议 URL 会通过 open-url 事件处理
  })
}

// 应用准备就绪
app.whenReady().then(() => {
  // 创建主窗口
  createMainWindow()

  // 启动 Web 端通信服务器
  webCommServer = createWebCommServer()
  
  // Windows/Linux: 处理命令行中的协议 URL（首次启动）
  if (process.platform === 'win32' || process.platform === 'linux') {
    const protocolUrl = process.argv.find(arg => arg.startsWith(`${PROTOCOL_NAME}://`))
    if (protocolUrl) {
      // 延迟处理，确保主窗口已加载
      // 增加延迟时间，确保主窗口完全准备好并加载完成
      setTimeout(() => {
        handleProtocolLaunch(protocolUrl).catch(error => {
          logger.error('首次启动处理协议失败:', error)
        })
      }, 400)
    }
  }

  // 自动更新：延迟检查更新（确保应用完全启动）
  // 仅在非开发环境下检查更新
  if (process.env.NODE_ENV !== 'development') {
    logger.info('[自动更新] 应用启动完成，5秒后自动检查更新')
    logger.info('[自动更新] 当前版本:', app.getVersion())
    logger.info('[自动更新] 更新服务器 URL:', autoUpdater.getFeedURL())
    
    setTimeout(() => {
      logger.info('[自动更新] 执行启动时自动检查更新...')
      autoUpdater.checkForUpdates().catch(err => {
        logger.error('[自动更新] 启动时检查更新失败:', err)
        logger.error('[自动更新] 错误信息:', err.message)
      })
    }, 3000)
    
    // 定期检查更新（每30分钟检查一次）
    logger.info('[自动更新] 已设置定期检查更新，间隔: 30分钟')
    setInterval(() => {
      logger.info('[自动更新] 执行定期检查更新...')
      autoUpdater.checkForUpdates().catch(err => {
        logger.error('[自动更新] 定期检查更新失败:', err)
        logger.error('[自动更新] 错误信息:', err.message)
      })
    }, 30 * 60 * 1000) // 30分钟
  } else {
    logger.info('[自动更新] 开发环境，跳过自动检查更新')
  }

  app.on('activate', () => {
    if (BrowserWindow.getAllWindows().length === 0) {
      createMainWindow()
    }
  })
})

// 所有窗口关闭时
app.on('window-all-closed', () => {
  // macOS 上通常应用会保持运行，除非用户明确退出
  if (process.platform !== 'darwin') {
    app.quit()
  }
})

// 应用退出前清理
app.on('before-quit', () => {
  // 关闭 Web 通信服务器
  if (webCommServer) {
    webCommServer.close()
  }

  // 清理所有标签页
  shopTabs.clear()
})

/**
 * 创建分离窗口（将标签页从原窗口分离到新窗口）
 * 使用店铺最开始的 URL 和 cookie 等入参，先新建窗口，然后用一键登录的逻辑重新加载
 * @param {string} shopId - 店铺ID
 * @param {string} shopName - 店铺名称（用于窗口标题）
 * @param {string} partition - partition ID
 * @param {string} url - 标签页URL
 * @returns {Promise<{success: boolean, message: string, windowId?: number}>}
 */
async function createDetachedWindow(shopId, shopName, partition, url) {
  try {
    // 获取标签页的原始信息（URL、cookies、cookieDomains 等）
    const tabInfo = shopTabs.get(shopId)
    if (!tabInfo) {
      return {
        success: false,
        message: `标签页不存在: ${shopId}`
      }
    }

    // 使用店铺名称作为窗口标题，创建分离窗口（isDetached=true）
    const windowTitle = shopName || tabInfo.shopName || `店铺-${shopId}`
    const newWindow = createWindow(null, windowTitle, true)
    const newWindowId = newWindow.windowId

    // 等待分离窗口加载完成
    await new Promise(resolve => {
      if (newWindow.webContents.isLoading()) {
        newWindow.webContents.once('did-finish-load', resolve)
      } else {
        resolve()
      }
    })

    // 等待 DOM 准备就绪（确保 detached.html 的脚本已经加载并准备好接收事件）
    await new Promise(resolve => setTimeout(resolve, 500))

    // 使用一键登录的逻辑重新加载网页
    // 先设置 Cookie（使用原来的 cookies 和 cookieDomains）
    // 注意：必须在 webview 加载 URL 之前设置 Cookie，这样 webview 加载时才能使用这些 Cookie
    const targetUrl = url || tabInfo.url || 'about:blank'
    const targetPartition = partition || tabInfo.partition
    
    if (tabInfo.cookies) {
      await setCookiesForWindow(
        targetPartition,
        targetUrl,
        tabInfo.cookies,
        tabInfo.cookieDomains
      )
    }

    // 更新 shopTabs 中的 windowId
    tabInfo.windowId = newWindowId
    shopTabs.set(shopId, tabInfo)

    // 通过 IPC 发送标签页信息到分离窗口
    // 分离窗口会接收这些信息并配置 webview（此时 Cookie 已经设置好了）
    newWindow.webContents.send('detached-tab-info', {
      shopId: shopId,
      shopName: windowTitle,
      partition: targetPartition,
      url: targetUrl
    })

    // 等待 webview 配置完成并加载
    await new Promise(resolve => setTimeout(resolve, 300))

    // 激活新窗口
    if (newWindow.isMinimized()) {
      newWindow.restore()
    }
    if (!newWindow.isVisible()) {
      newWindow.show()
    }
    newWindow.focus()

    return {
      success: true,
      message: '标签页已分离到新窗口',
      windowId: newWindowId
    }
  } catch (error) {
    logger.error(`创建分离窗口失败:`, error)
    return {
      success: false,
      message: `创建分离窗口失败: ${error.message}`
    }
  }
}

// IPC 处理器
ipcMain.handle('close-shop-window', (event, shopId) => {
  return closeShopTab(shopId)
})

ipcMain.handle('detach-tab-to-window', async (event, data) => {
  const { shopId, shopName, partition, url } = data
  const tabInfo = shopTabs.get(shopId)
  
  if (!tabInfo) {
    return {
      success: false,
      message: `标签页不存在: ${shopId}`
    }
  }

  // 使用标签页的实际信息
  const actualShopName = shopName || tabInfo.shopName || `店铺-${shopId}`
  const actualPartition = partition || tabInfo.partition
  const actualUrl = url || tabInfo.url || 'about:blank'

  return await createDetachedWindow(shopId, actualShopName, actualPartition, actualUrl)
})

ipcMain.handle('get-opened-shops', () => {
  const openedShops = []
  shopTabs.forEach((tabInfo, shopId) => {
    openedShops.push({
      shopId,
      shopName: tabInfo.shopName,
      url: tabInfo.url
    })
  })
  return openedShops
})
